<!DOCTYPE html>
<html lang="en">

<head>

    <meta charset="UTF-8">

    <!-- <link rel="apple-touch-icon" type="image/png"
        href="https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" /> -->
    <meta name="apple-mobile-web-app-title" content="CodePen">

    <!-- <link rel="shortcut icon" type="image/x-icon"
        href="https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />

    <link rel="mask-icon" type=""
        href="https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg"
        color="#111" /> -->


    <title>CodePen - ios datepicker (pure js)</title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">



    <style>
        @charset "UTF-8";

        body {
            font-family: Arial, "Hiragino Sans GB", 冬青黑, "Microsoft YaHei", 微软雅黑, SimSun, 宋体, Helvetica, Tahoma, "Arial sans-serif";
            background: #000;
        }

        .select-wrap {
            position: relative;
            height: 100%;
            text-align: center;
            overflow: hidden;
            font-size: 20px;
            color: #ddd;
        }

        .select-wrap:before,
        .select-wrap:after {
            position: absolute;
            z-index: 1;
            display: block;
            content: "";
            width: 100%;
            height: 50%;
        }

        .select-wrap:before {
            top: 0;
            background-image: linear-gradient(to bottom, rgba(1, 1, 1, 0.5), rgba(1, 1, 1, 0));
        }

        .select-wrap:after {
            bottom: 0;
            background-image: linear-gradient(to top, rgba(1, 1, 1, 0.5), rgba(1, 1, 1, 0));
        }

        .select-wrap .select-options {
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 0;
            transform-style: preserve-3d;
            margin: 0 auto;
            display: block;
            transform: translateZ(-150px) rotateX(0deg);
            -webkit-font-smoothing: subpixel-antialiased;
            color: #666;
        }

        .select-wrap .select-options .select-option {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 50px;
            -webkit-font-smoothing: subpixel-antialiased;
        }

        .select-wrap .select-options .select-option:nth-child(1) {
            transform: rotateX(0deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(2) {
            transform: rotateX(-18deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(3) {
            transform: rotateX(-36deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(4) {
            transform: rotateX(-54deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(5) {
            transform: rotateX(-72deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(6) {
            transform: rotateX(-90deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(7) {
            transform: rotateX(-108deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(8) {
            transform: rotateX(-126deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(9) {
            transform: rotateX(-144deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(10) {
            transform: rotateX(-162deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(11) {
            transform: rotateX(-180deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(12) {
            transform: rotateX(-198deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(13) {
            transform: rotateX(-216deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(14) {
            transform: rotateX(-234deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(15) {
            transform: rotateX(-252deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(16) {
            transform: rotateX(-270deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(17) {
            transform: rotateX(-288deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(18) {
            transform: rotateX(-306deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(19) {
            transform: rotateX(-324deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(20) {
            transform: rotateX(-342deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(21) {
            transform: rotateX(-360deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(22) {
            transform: rotateX(-378deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(23) {
            transform: rotateX(-396deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(24) {
            transform: rotateX(-414deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(25) {
            transform: rotateX(-432deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(26) {
            transform: rotateX(-450deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(27) {
            transform: rotateX(-468deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(28) {
            transform: rotateX(-486deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(29) {
            transform: rotateX(-504deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(30) {
            transform: rotateX(-522deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(31) {
            transform: rotateX(-540deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(32) {
            transform: rotateX(-558deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(33) {
            transform: rotateX(-576deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(34) {
            transform: rotateX(-594deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(35) {
            transform: rotateX(-612deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(36) {
            transform: rotateX(-630deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(37) {
            transform: rotateX(-648deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(38) {
            transform: rotateX(-666deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(39) {
            transform: rotateX(-684deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(40) {
            transform: rotateX(-702deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(41) {
            transform: rotateX(-720deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(42) {
            transform: rotateX(-738deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(43) {
            transform: rotateX(-756deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(44) {
            transform: rotateX(-774deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(45) {
            transform: rotateX(-792deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(46) {
            transform: rotateX(-810deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(47) {
            transform: rotateX(-828deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(48) {
            transform: rotateX(-846deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(49) {
            transform: rotateX(-864deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(50) {
            transform: rotateX(-882deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(51) {
            transform: rotateX(-900deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(52) {
            transform: rotateX(-918deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(53) {
            transform: rotateX(-936deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(54) {
            transform: rotateX(-954deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(55) {
            transform: rotateX(-972deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(56) {
            transform: rotateX(-990deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(57) {
            transform: rotateX(-1008deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(58) {
            transform: rotateX(-1026deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(59) {
            transform: rotateX(-1044deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(60) {
            transform: rotateX(-1062deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(61) {
            transform: rotateX(-1080deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(62) {
            transform: rotateX(-1098deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(63) {
            transform: rotateX(-1116deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(64) {
            transform: rotateX(-1134deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(65) {
            transform: rotateX(-1152deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(66) {
            transform: rotateX(-1170deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(67) {
            transform: rotateX(-1188deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(68) {
            transform: rotateX(-1206deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(69) {
            transform: rotateX(-1224deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(70) {
            transform: rotateX(-1242deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(71) {
            transform: rotateX(-1260deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(72) {
            transform: rotateX(-1278deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(73) {
            transform: rotateX(-1296deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(74) {
            transform: rotateX(-1314deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(75) {
            transform: rotateX(-1332deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(76) {
            transform: rotateX(-1350deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(77) {
            transform: rotateX(-1368deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(78) {
            transform: rotateX(-1386deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(79) {
            transform: rotateX(-1404deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(80) {
            transform: rotateX(-1422deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(81) {
            transform: rotateX(-1440deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(82) {
            transform: rotateX(-1458deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(83) {
            transform: rotateX(-1476deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(84) {
            transform: rotateX(-1494deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(85) {
            transform: rotateX(-1512deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(86) {
            transform: rotateX(-1530deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(87) {
            transform: rotateX(-1548deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(88) {
            transform: rotateX(-1566deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(89) {
            transform: rotateX(-1584deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(90) {
            transform: rotateX(-1602deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(91) {
            transform: rotateX(-1620deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(92) {
            transform: rotateX(-1638deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(93) {
            transform: rotateX(-1656deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(94) {
            transform: rotateX(-1674deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(95) {
            transform: rotateX(-1692deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(96) {
            transform: rotateX(-1710deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(97) {
            transform: rotateX(-1728deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(98) {
            transform: rotateX(-1746deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(99) {
            transform: rotateX(-1764deg) translateZ(150px);
        }

        .select-wrap .select-options .select-option:nth-child(100) {
            transform: rotateX(-1782deg) translateZ(150px);
        }

        .highlight {
            position: absolute;
            top: 50%;
            transform: translate(0, -50%);
            width: 100%;
            background-color: #000;
            border-top: 1px solid #333;
            border-bottom: 1px solid #333;
            font-size: 24px;
            overflow: hidden;
        }

        .highlight-list {
            position: absolute;
            width: 100%;
        }

        /* date */
        .date-selector {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            perspective: 2000px;
            display: flex;
            align-items: stretch;
            justify-content: space-between;
            width: 600px;
            height: 300px;
        }

        .date-selector>div {
            flex: 1;
        }

        .date-selector .select-wrap {
            font-size: 18px;
        }

        .date-selector .highlight {
            font-size: 20px;
        }
    </style>

    <script>
        window.console = window.console || function (t) { };
    </script>



    <script>
        if (document.location.search.match(/type=embed/gi)) {
            window.parent.postMessage("resize", "*");
        }
    </script>


</head>

<body translate="no">
    <div class="date-selector">
        <div class="year" id="year1"></div>
        <div class="month" id="month1"></div>
        <div class="day" id="day1"></div>
    </div>

    <!-- <div class="hour-selector">
	<div class="hour" id="hour"></div>
	<div class="minute" id="minute"></div>
</div> -->
    <script
        src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js"></script>


    <script id="rendered-js">
        const easing = {
            easeOutCubic: function (pos) {
                return Math.pow(pos - 1, 3) + 1;
            },
            easeOutQuart: function (pos) {
                return -(Math.pow(pos - 1, 4) - 1);
            }
        };


        class IosSelector {
            constructor(options) {
                let defaults = {
                    el: '', // dom 
                    type: 'infinite', // infinite 无限滚动，normal 非无限 
                    count: 20, // 圆环规格，圆环上选项个数，必须设置 4 的倍数
                    sensitivity: 0.8, // 灵敏度
                    source: [], // 选项 {value: xx, text: xx}
                    value: null,
                    onChange: null
                };


                this.options = Object.assign({}, defaults, options);
                this.options.count = this.options.count - this.options.count % 4;
                Object.assign(this, this.options);

                this.halfCount = this.options.count / 2;
                this.quarterCount = this.options.count / 4;
                this.a = this.options.sensitivity * 10; // 滚动减速度
                this.minV = Math.sqrt(1 / this.a); // 最小初速度
                this.selected = this.source[0];

                this.exceedA = 10; // 超出减速 
                this.moveT = 0; // 滚动 tick
                this.moving = false;

                this.elems = {
                    el: document.querySelector(this.options.el),
                    circleList: null,
                    circleItems: null, // list

                    highlight: null,
                    highlightList: null,
                    highListItems: null // list
                };
                this.events = {
                    touchstart: null,
                    touchmove: null,
                    touchend: null
                };


                this.itemHeight = this.elems.el.offsetHeight * 3 / this.options.count; // 每项高度
                this.itemAngle = 360 / this.options.count; // 每项之间旋转度数
                this.radius = this.itemHeight / Math.tan(this.itemAngle * Math.PI / 180); // 圆环半径 

                this.scroll = 0; // 单位为一个 item 的高度（度数）
                this._init();
            }

            _init() {
                this._create(this.options.source);

                let touchData = {
                    startY: 0,
                    yArr: []
                };


                for (let eventName in this.events) {
                    this.events[eventName] = (eventName => {
                        return e => {
                            if (this.elems.el.contains(e.target) || e.target === this.elems.el) {
                                e.preventDefault();
                                if (this.source.length) {
                                    this['_' + eventName](e, touchData);
                                }
                            }
                        };
                    })(eventName);
                }

                this.elems.el.addEventListener('touchstart', this.events.touchstart);
                document.addEventListener('mousedown', this.events.touchstart);
                this.elems.el.addEventListener('touchend', this.events.touchend);
                document.addEventListener('mouseup', this.events.touchend);
                if (this.source.length) {
                    this.value = this.value !== null ? this.value : this.source[0].value;
                    this.select(this.value);
                }
            }

            _touchstart(e, touchData) {
                this.elems.el.addEventListener('touchmove', this.events.touchmove);
                document.addEventListener('mousemove', this.events.touchmove);
                let eventY = e.clientY || e.touches[0].clientY;
                touchData.startY = eventY;
                touchData.yArr = [[eventY, new Date().getTime()]];
                touchData.touchScroll = this.scroll;
                this._stop();
            }

            _touchmove(e, touchData) {
                let eventY = e.clientY || e.touches[0].clientY;
                touchData.yArr.push([eventY, new Date().getTime()]);
                if (touchData.length > 5) {
                    touchData.unshift();
                }

                let scrollAdd = (touchData.startY - eventY) / this.itemHeight;
                let moveToScroll = scrollAdd + this.scroll;

                // 非无限滚动时，超出范围使滚动变得困难
                if (this.type === 'normal') {
                    if (moveToScroll < 0) {
                        moveToScroll *= 0.3;
                    } else if (moveToScroll > this.source.length) {
                        moveToScroll = this.source.length + (moveToScroll - this.source.length) * 0.3;
                    }
                    // console.log(moveToScroll);
                } else {
                    moveToScroll = this._normalizeScroll(moveToScroll);
                }

                touchData.touchScroll = this._moveTo(moveToScroll);
            }

            _touchend(e, touchData) {
                // console.log(e);
                this.elems.el.removeEventListener('touchmove', this.events.touchmove);
                document.removeEventListener('mousemove', this.events.touchmove);

                let v;

                if (touchData.yArr.length === 1) {
                    v = 0;
                } else {
                    let startTime = touchData.yArr[touchData.yArr.length - 2][1];
                    let endTime = touchData.yArr[touchData.yArr.length - 1][1];
                    let startY = touchData.yArr[touchData.yArr.length - 2][0];
                    let endY = touchData.yArr[touchData.yArr.length - 1][0];

                    // 计算速度
                    v = (startY - endY) / this.itemHeight * 1000 / (endTime - startTime);
                    let sign = v > 0 ? 1 : -1;

                    v = Math.abs(v) > 30 ? 30 * sign : v;
                }

                this.scroll = touchData.touchScroll;
                this._animateMoveByInitV(v);

                // console.log('end');
            }

            _create(source) {

                if (!source.length) {
                    return;
                }

                let template = `
                <div class="select-wrap">
                    <ul class="select-options" style="transform: translate3d(0, 0, ${-this.radius}px) rotateX(0deg);">
                    {{circleListHTML}}
                    <!-- <li class="select-option">a0</li> -->
                    </ul>
                    <div class="highlight">
                    <ul class="highlight-list">
                        <!-- <li class="highlight-item"></li> -->
                        {{highListHTML}}
                    </ul>
                    </div>
                </div>
                `;

                // source 处理
                if (this.options.type === 'infinite') {
                    let concatSource = [].concat(source);
                    while (concatSource.length < this.halfCount) {
                        concatSource = concatSource.concat(source);
                    }
                    source = concatSource;
                }
                this.source = source;
                let sourceLength = source.length;

                // 圆环 HTML
                let circleListHTML = '';
                for (let i = 0; i < source.length; i++) {
                    circleListHTML += `<li class="select-option"
                    style="
                      top: ${this.itemHeight * -0.5}px;
                      height: ${this.itemHeight}px;
                      line-height: ${this.itemHeight}px;
                      transform: rotateX(${-this.itemAngle * i}deg) translate3d(0, 0, ${this.radius}px);
                    "
                    data-index="${i}"
                    >${source[i].text}</li>`;
                }

                // 中间高亮 HTML
                let highListHTML = '';
                for (let i = 0; i < source.length; i++) {
                    highListHTML += `<li class="highlight-item" style="height: ${this.itemHeight}px;">
                        ${source[i].text}
                      </li>`;
                }


                if (this.options.type === 'infinite') {

                    // 圆环头尾
                    for (let i = 0; i < this.quarterCount; i++) {
                        // 头
                        circleListHTML = `<li class="select-option"
                      style="
                        top: ${this.itemHeight * -0.5}px;
                        height: ${this.itemHeight}px;
                        line-height: ${this.itemHeight}px;
                        transform: rotateX(${this.itemAngle * (i + 1)}deg) translate3d(0, 0, ${this.radius}px);
                      "
                      data-index="${-i - 1}"
                      >${source[sourceLength - i - 1].text}</li>` + circleListHTML;
                        // 尾
                        circleListHTML += `<li class="select-option"
                      style="
                        top: ${this.itemHeight * -0.5}px;
                        height: ${this.itemHeight}px;
                        line-height: ${this.itemHeight}px;
                        transform: rotateX(${-this.itemAngle * (i + sourceLength)}deg) translate3d(0, 0, ${this.radius}px);
                      "
                      data-index="${i + sourceLength}"
                      >${source[i].text}</li>`;
                    }

                    // 高亮头尾
                    highListHTML = `<li class="highlight-item" style="height: ${this.itemHeight}px;">
                          ${source[sourceLength - 1].text}
                      </li>` + highListHTML;
                    highListHTML += `<li class="highlight-item" style="height: ${this.itemHeight}px;">${source[0].text}</li>`;
                }

                this.elems.el.innerHTML = template.
                    replace('{{circleListHTML}}', circleListHTML).
                    replace('{{highListHTML}}', highListHTML);
                this.elems.circleList = this.elems.el.querySelector('.select-options');
                this.elems.circleItems = this.elems.el.querySelectorAll('.select-option');


                this.elems.highlight = this.elems.el.querySelector('.highlight');
                this.elems.highlightList = this.elems.el.querySelector('.highlight-list');
                this.elems.highlightitems = this.elems.el.querySelectorAll('.highlight-item');

                if (this.type === 'infinite') {
                    this.elems.highlightList.style.top = -this.itemHeight + 'px';
                }

                this.elems.highlight.style.height = this.itemHeight + 'px';
                this.elems.highlight.style.lineHeight = this.itemHeight + 'px';

            }

            /**
             * 对 scroll 取模，eg source.length = 5 scroll = 6.1 
             * 取模之后 normalizedScroll = 1.1
             * @param {init} scroll 
             * @return 取模之后的 normalizedScroll
             */
            _normalizeScroll(scroll) {
                let normalizedScroll = scroll;

                while (normalizedScroll < 0) {
                    normalizedScroll += this.source.length;
                }
                normalizedScroll = normalizedScroll % this.source.length;
                return normalizedScroll;
            }

            /**
             * 定位到 scroll，无动画
             * @param {init} scroll 
             * @return 返回指定 normalize 之后的 scroll
             */
            _moveTo(scroll) {
                if (this.type === 'infinite') {
                    scroll = this._normalizeScroll(scroll);
                }
                this.elems.circleList.style.transform = `translate3d(0, 0, ${-this.radius}px) rotateX(${this.itemAngle * scroll}deg)`;
                this.elems.highlightList.style.transform = `translate3d(0, ${-scroll * this.itemHeight}px, 0)`;

                [...this.elems.circleItems].forEach(itemElem => {
                    if (Math.abs(itemElem.dataset.index - scroll) > this.quarterCount) {
                        itemElem.style.visibility = 'hidden';
                    } else {
                        itemElem.style.visibility = 'visible';
                    }
                });

                // console.log(scroll);
                // console.log(`translate3d(0, 0, ${-this.radius}px) rotateX(${-this.itemAngle * scroll}deg)`);
                return scroll;
            }

            /**
             * 以初速度 initV 滚动
             * @param {init} initV， initV 会被重置
             * 以根据加速度确保滚动到整数 scroll (保证能通过 scroll 定位到一个选中值)
             */
            async _animateMoveByInitV(initV) {

                // console.log(initV);

                let initScroll;
                let finalScroll;
                let finalV;

                let totalScrollLen;
                let a;
                let t;

                if (this.type === 'normal') {

                    if (this.scroll < 0 || this.scroll > this.source.length - 1) {
                        a = this.exceedA;
                        initScroll = this.scroll;
                        finalScroll = this.scroll < 0 ? 0 : this.source.length - 1;
                        totalScrollLen = initScroll - finalScroll;

                        t = Math.sqrt(Math.abs(totalScrollLen / a));
                        initV = a * t;
                        initV = this.scroll > 0 ? -initV : initV;
                        finalV = 0;
                        await this._animateToScroll(initScroll, finalScroll, t);
                    } else {
                        initScroll = this.scroll;
                        a = initV > 0 ? -this.a : this.a; // 减速加速度
                        t = Math.abs(initV / a); // 速度减到 0 花费时间
                        totalScrollLen = initV * t + a * t * t / 2; // 总滚动长度
                        finalScroll = Math.round(this.scroll + totalScrollLen); // 取整，确保准确最终 scroll 为整数
                        finalScroll = finalScroll < 0 ? 0 : finalScroll > this.source.length - 1 ? this.source.length - 1 : finalScroll;

                        totalScrollLen = finalScroll - initScroll;
                        t = Math.sqrt(Math.abs(totalScrollLen / a));
                        await this._animateToScroll(this.scroll, finalScroll, t, 'easeOutQuart');
                    }

                } else {
                    initScroll = this.scroll;

                    a = initV > 0 ? -this.a : this.a; // 减速加速度
                    t = Math.abs(initV / a); // 速度减到 0 花费时间
                    totalScrollLen = initV * t + a * t * t / 2; // 总滚动长度
                    finalScroll = Math.round(this.scroll + totalScrollLen); // 取整，确保准确最终 scroll 为整数
                    await this._animateToScroll(this.scroll, finalScroll, t, 'easeOutQuart');
                }

                // await this._animateToScroll(this.scroll, finalScroll, initV, 0);

                this._selectByScroll(this.scroll);
            }

            _animateToScroll(initScroll, finalScroll, t, easingName = 'easeOutQuart') {
                if (initScroll === finalScroll || t === 0) {
                    this._moveTo(initScroll);
                    return;
                }

                let start = new Date().getTime() / 1000;
                let pass = 0;
                let totalScrollLen = finalScroll - initScroll;

                // console.log(initScroll, finalScroll, initV, finalV, a);
                return new Promise((resolve, reject) => {
                    this.moving = true;
                    let tick = () => {
                        pass = new Date().getTime() / 1000 - start;

                        if (pass < t) {
                            this.scroll = this._moveTo(initScroll + easing[easingName](pass / t) * totalScrollLen);
                            this.moveT = requestAnimationFrame(tick);
                        } else {
                            resolve();
                            this._stop();
                            this.scroll = this._moveTo(initScroll + totalScrollLen);
                        }
                    };
                    tick();
                });
            }

            _stop() {
                this.moving = false;
                cancelAnimationFrame(this.moveT);
            }

            _selectByScroll(scroll) {
                scroll = this._normalizeScroll(scroll) | 0;
                if (scroll > this.source.length - 1) {
                    scroll = this.source.length - 1;
                    this._moveTo(scroll);
                }
                this._moveTo(scroll);
                this.scroll = scroll;
                this.selected = this.source[scroll];
                this.value = this.selected.value;
                this.onChange && this.onChange(this.selected);
            }

            updateSource(source) {
                this._create(source);

                if (!this.moving) {
                    this._selectByScroll(this.scroll);
                }
            }

            select(value) {
                for (let i = 0; i < this.source.length; i++) {
                    if (this.source[i].value === value) {
                        window.cancelAnimationFrame(this.moveT);
                        // this.scroll = this._moveTo(i);
                        let initScroll = this._normalizeScroll(this.scroll);
                        let finalScroll = i;
                        let t = Math.sqrt(Math.abs((finalScroll - initScroll) / this.a));
                        this._animateToScroll(initScroll, finalScroll, t);
                        setTimeout(() => this._selectByScroll(i));
                        return;
                    }
                }
                throw new Error(`can not select value: ${value}, ${value} match nothing in current source`);
            }

            destroy() {
                this._stop();
                // document 事件解绑
                for (let eventName in this.events) {
                    this.elems.el.removeEventListener('eventName', this.events[eventName]);
                }
                document.removeEventListener('mousedown', this.events['touchstart']);
                document.removeEventListener('mousemove', this.events['touchmove']);
                document.removeEventListener('mouseup', this.events['touchend']);
                // 元素移除
                this.elems.el.innerHTML = '';
                this.elems = null;
            }
        }



        // date logic


        function getYears() {
            let currentYear = new Date().getFullYear();
            let years = [];

            for (let i = currentYear - 20; i < currentYear + 20; i++) {
                years.push({
                    value: i,
                    text: i + '年'
                });

            }
            return years;
        }

        function getMonths(year) {
            let months = [];
            for (let i = 1; i <= 12; i++) {
                months.push({
                    value: i,
                    text: i + '月'
                });

            }
            return months;
        }

        function getDays(year, month) {
            let dayCount = new Date(year, month, 0).getDate();
            let days = [];

            for (let i = 1; i <= dayCount; i++) {
                days.push({
                    value: i,
                    text: i + '日'
                });

            }

            return days;
        }

        let currentYear = new Date().getFullYear();
        let currentMonth = 1;
        let currentDay = 1;

        let yearSelector;
        let monthSelector;
        let daySelector;

        yearSource = getYears();
        monthSource = getMonths();
        daySource = getDays(currentYear, currentMonth);

        yearSelector = new IosSelector({
            el: '#year1',
            type: 'infinite',
            source: yearSource,
            count: 20,
            onChange: selected => {
                currentYear = selected.value;
                daySource = getDays(currentYear, currentMonth);
                daySelector.updateSource(daySource);
                console.log(yearSelector.value, monthSelector.value, daySelector.value);
            }
        });


        monthSelector = new IosSelector({
            el: '#month1',
            type: 'infinite',
            source: monthSource,
            count: 20,
            onChange: selected => {
                currentMonth = selected.value;

                daySource = getDays(currentYear, currentMonth);
                daySelector.updateSource(daySource);
                console.log(yearSelector.value, monthSelector.value, daySelector.value);
            }
        });


        daySelector = new IosSelector({
            el: '#day1',
            type: 'infinite',
            source: [],
            count: 20,
            onChange: selected => {
                currentDay = selected.value;
                console.log(yearSelector.value, monthSelector.value, daySelector.value);
            }
        });

        let now = new Date();


        setTimeout(function () {
            yearSelector.select(now.getFullYear());
            monthSelector.select(now.getMonth() + 1);
            daySelector.select(now.getDate());
        });

    </script>



</body>

</html>